package com.manager.common.dao;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.*;

import javax.sql.DataSource;

import com.manager.common.annotation.DictionaryInfoGroupId;
import com.manager.common.utils.ClassUtils;
import com.manager.common.utils.DataTools;
import com.manager.common.utils.ObjectUtils;
import com.manager.model.AllId;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.ManagedType;
import jakarta.persistence.metamodel.Metamodel;
import jakarta.persistence.metamodel.SingularAttribute;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.SingleTableEntityPersister;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.Query;
import org.hibernate.transform.Transformers;
import org.hibernate.type.BasicTypeReference;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Component;

import java.lang.Double;

@Component("superDao")
@SuppressWarnings("all")
public class SuperDao<T> {

    @Autowired
    public Session session;

    @Qualifier("dataSource")
    @Autowired
    private DataSource dataSource;

    private static Map<String, Map<String, String>> propertyMapping = new HashMap<>();

    private static Map<String, Map<String, Field>> fieldMapping = new HashMap<>();

    /**
     * 增加一条数据
     *
     * @param t
     */
    public void addT(T t) {
       this.session.save(t);
    }

    public void batchAddT(List<?> list) {
        for (Object t : list) {
            this.session.save(t);
        }
    }

    /**
     * 更新一条数据
     *
     * @param t
     */
    public void updateT(T t) {
       this.session.update(t);
    }

    /**
     * 删除一条数据
     *
     * @param t
     */
    public void delete(T t) {
       this.session.delete(t);
    }

    public void batchDelete(List<?> list) {

        if (list == null || list.size() <= 0) {
            return;
        }

        for (Object t : list) {
           this.session.delete(t);
        }
    }

    public void excuteSQL(String sql) {
       this.session.createNativeQuery(sql).executeUpdate();

    }

    public void excuteSQL(String sql, Object[] params) {
        Query query =this.session.createNativeQuery(sql);
        for (int i = 0; i < params.length; i++) {
            query.setParameter(i+1, params[i]);
        }
        query.executeUpdate();

    }

    public void excute(String hql) {
       this.session.createQuery(hql).executeUpdate();
    }

    public void excute(String hql, Object[] params) {
        Query query =this.session.createQuery(hql);
        for (int i = 0; i < params.length; i++) {
            query.setParameter(i+1, params[i]);
        }
        query.executeUpdate();
    }

    /**
     * 加载一条数据
     *
     * @param t
     * @param id
     */
    public T getT(T t, String id) {
        return (T)this.session
                .get(t.getClass(), id);
    }

    public <T> T getT(String id, Class<T> claz) {
        return (T)this.session
                .get(claz, id);
    }

    public <T> T getT(Class<T> claz, String id) {
        return (T)this.session
                .get(claz, id);
    }

    /**
     * 加载一条数据
     *
     * @param t
     * @param id
     */
    public T getT(T t, Integer id) {
        return (T)this.session
                .get(t.getClass(), id);
    }

    /**
     * 加载一条件数
     *
     * @param t
     * @param id
     */
    public T loadT(T t, String id) {
        return (T)this.session
                .load(t.getClass(), id);
    }

    public <T> T loadT(String id, Class<T> claz) {
        return (T)this.session
                .load(claz, id);
    }

    /**
     * 加载一条件数
     *
     * @param t
     * @param id
     */
    public T loadT(T t, Integer id) {
        return (T)this.session
                .load(t.getClass(), id);
    }

    /**
     * 分页查询数据
     *
     * @param hql   hsql语句
     * @param start 开始数
     * @param limit 查多少条
     * @return
     */
    public List getList(String hql, int start, int limit, String desc) {
        String temp = desc.substring(desc.lastIndexOf(desc.trim()));// 去前空格
        if (null != temp && !"".equals(temp)) {
            if (temp.equals("order by t.")) {
                hql += "  order by t.createTime desc";
            } else {
                hql += "  " + temp;
            }
        } else {
            hql += "  order by t.createTime desc";
        }
        Query q =this.session
                .createQuery(hql);
        if (start != 0) {
            q.setFirstResult(start);
        }
        if (limit != 0) {
            q.setMaxResults(limit);
        }
        return q.list();
    }

    /**
     * 分页查询数据，有条件和参数
     *
     * @param hql   hql语句
     * @param obj   参数
     * @param start 开始数
     * @param limit 查多少条
     * @return
     */
    public List getList(String hql, Object[] obj, int start, int limit, String desc) {
        String temp = desc.substring(desc.lastIndexOf(desc.trim()));// 去前空格
        if (null != temp && !"".equals(temp)) {
            if (temp.equals("order by t.")) {
                hql += "  order by t.createTime desc";
            } else if (temp.equals("desc") || temp.equals("asc")) {
                hql += "  order by t.createTime " + temp;
            } else {
                hql += "  " + temp;
            }
        } else {
            hql += "  order by t.createTime desc";
        }
        Query query =this.session
                .createQuery(hql);
        if (obj != null) {
            for (int i = 0; i < obj.length; i++) {
                query.setParameter(i+1, obj[i]);
            }
        }
        if (start != 0) {
            query.setFirstResult(start);
        }
        if (limit != 0) {
            query.setMaxResults(limit);
        }
        return query.list();
    }

    /**
     * 分页查询数据，有条件和参数
     *
     * @param hql   hql语句
     * @param obj   参数
     * @param start 开始数
     * @param limit 查多少条
     * @return
     */
    public List getList(String hql, Object[] obj, int start, int limit) {
        Query query =this.session
                .createQuery(hql);
        if (obj != null) {
            for (int i = 0; i < obj.length; i++) {
                query.setParameter(i+1, obj[i]);
            }
        }
        if (start != 0) {
            query.setFirstResult(start);
        }
        if (limit != 0) {
            query.setMaxResults(limit);
        }
        return query.list();
    }

    /**
     * 分页查询数据，有条件和参数
     *
     * @param hql   hql语句
     * @param obj   参数
     * @param start 开始数
     * @param limit 查多少条
     * @return
     */
    public T getListOne(String hql, Object[] obj, String desc) {
        if (null != desc && !"".equals(desc)) {
            hql += "  " + desc;
        } else {
            hql += "  order by t.createTime desc";
        }

        Query query =this.session
                .createQuery(hql).setFirstResult(0).setMaxResults(1);
        if (obj != null) {
            for (int i = 0; i < obj.length; i++) {
                query.setParameter(i+1, obj[i]);
            }
        }
        List<T> list = query.list();
        if (null == list || list.size() <= 0) {
            return null;
        } else {
            return list.get(0);
        }
    }

    public <T> T getListOne(String hql, Object[] obj, String desc, Class<T> claz) {
        if (null != desc && !"".equals(desc)) {
            hql += "  " + desc;
        } else {
            hql += "  order by t.createTime desc";
        }
        Query query =this.session
                .createQuery(hql).setFirstResult(0).setMaxResults(1);
        if (obj != null) {
            for (int i = 0; i < obj.length; i++) {
                query.setParameter(i+1, obj[i]);
            }
        }
        List<T> list = query.list();
        if (null == list || list.size() <= 0) {
            return null;
        } else {
            return (T) list.get(0);
        }
    }

    /**
     * 分页查询数据总数
     *
     * @param hql
     * @param desc
     * @return
     */
    public int getListCount(String hql, String desc) {

        return Integer.valueOf(this.session
                .createQuery(hql).uniqueResult().toString());
    }

    ;

    /**
     * 分页查询数据，有条件和参数
     *
     * @param hql   hql语句
     * @param obj   参数
     * @param start 开始数
     * @param limit 查多少条
     * @return
     */
    public int getListCount(String hql, Object[] obj, String desc) {

        Query query =this.session
                .createQuery(hql);
        if (obj != null) {
            for (int i = 0; i < obj.length; i++) {
                query.setParameter(i+1, obj[i]);
            }
        }
        return Integer.parseInt(query.list().get(0).toString());
    }

    /**
     * 分页查询数据，有条件和参数
     *
     * @param hql   hql语句
     * @param obj   参数
     * @param start 开始数
     * @param limit 查多少条
     * @return
     */
    public int getListCount(String hql, Object[] obj) {
        Query query =this.session
                .createQuery(hql);
        if (obj != null && obj.length > 0) {
            for (int i = 0; i < obj.length; i++) {
                query.setParameter(i+1, obj[i]);
            }
        }
        return Integer.parseInt(query.list().get(0).toString());
    }

    public int deleteTSql(String hql, Object[] obj) {
        Query query =this.session
                .createQuery(hql);
        if (null != obj) {
            for (int i = 0; i < obj.length; i++) {
                query.setParameter(i+1, obj[i]);
            }
        }
        return query.executeUpdate();
    }

    public int deleteTSql(String hql) {
        return this.session
                .createNativeQuery(hql).executeUpdate();

    }

    /**
     * 更新
     *
     * @param hql
     * @param obj
     * @return
     */
    public int exectUpdate(String hql, Object[] obj) {
        Query query =this.session
                .createQuery(hql);
        if (obj != null) {
            for (int i = 0; i < obj.length; i++) {
                query.setParameter(i+1, obj[i]);
            }
        }
        return query.executeUpdate();

    }

    /**
     * 分页查询数据
     *
     * @param hql   hsql语句
     * @param start 开始数
     * @param limit 查多少条
     * @return
     */
    public List getListSql(String sql, Object[] obj, int start, int limit, String desc) {
        if (null != desc && !"".equals(desc)) {
            sql += "  " + desc;
        }
        Query q =this.session
                .createNativeQuery(sql);
        if (obj != null) {
            for (int i = 0; i < obj.length; i++) {
                q.setParameter(i+1, obj[i]);
            }
        }
        if (start != 0) {
            q.setFirstResult(start);
        }
        if (limit != 0) {
            q.setMaxResults(limit);
        }
        return q.list();
    }

    /**
     * TODO 分页查询,返回Key:Value形式的数据
     *
     * @param sql   SQL语句
     * @param obj   参数
     * @param start 开始数
     * @param limit 查多少条
     * @param desc  顺序
     * @return
     */
    public List<Map<String, Object>> getKVListSql(String sql, Object[] obj, int start, int limit, String desc) {
        String temp = desc.substring(desc.lastIndexOf(desc.trim()));// 去前后空格
        if (null != temp && !"".equals(temp)) {
            if (temp.startsWith("order by")) {
                if (temp.equals("order by t.")) {
                    sql += "  order by t.CREATE_TIME desc";
                } else {
                    sql += "  " + temp;
                }
            } else {
                sql += "  order by t." + temp;
            }
        } else {
            sql += "  order by t.CREATE_TIME desc";
        }
        /*
         * if (null != desc && !"".equals(desc)) { sql += "  " + desc; }
         */
        Query q =this.session
                .createNativeQuery(sql);
        if (obj != null) {
            for (int i = 0; i < obj.length; i++) {
                q.setParameter(i+1, obj[i]);
            }
        }
        if (start != 0) {
            q.setFirstResult(start);
        }
        if (limit != 0) {
            q.setMaxResults(limit);
        }
        return q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
    }

    public <T> List<T> getKVListHql(String hql, Object[] obj, int start, int limit, String desc, Class<T> claz) throws Exception {
        String temp = desc.substring(desc.lastIndexOf(desc.trim()));// 去前后空格
        if (null != temp && !"".equals(temp)) {
            if (temp.startsWith("order by")) {
                if (temp.equals("order by t.")) {
                    hql += "  order by t.createTime desc";
                } else {
                    hql += "  " + temp;
                }
            } else {
                hql += "  order by t." + temp;
            }
        } else {
            hql += "  order by t.createTime desc";
        }
        /*
         * if (null != desc && !"".equals(desc)) { sql += "  " + desc; }
         */
        Query q =this.session
                .createQuery(hql);
        if (obj != null) {
            for (int i = 0; i < obj.length; i++) {
                q.setParameter(i+1, obj[i]);
            }
        }
        if (start != 0) {
            q.setFirstResult(start);
        }
        if (limit != 0) {
            q.setMaxResults(limit);
        }
        List<T> r = new ArrayList<>();
        List<Object[]> results = q.list();
        Map<String, Field> fieldMap = this.getClazFieldMap(claz);
        List<String> fieldNames = ObjectUtils.pareseSelectSQL(hql);
        for (Object[] result : results) {
            Object object = claz.newInstance();
            for (int i = 0; i < fieldNames.size(); i++) {
                Field field = fieldMap.get(fieldNames.get(i).toLowerCase());
                ClassUtils.setValue(result[i], object, field);
            }
            r.add((T) object);
        }
        return r;
    }

    /**
     * sql 查询统计
     *
     * @param hql
     * @param obj
     * @param desc
     * @return
     */
    public int getListCountSql(String sql, Object[] obj, String desc) {
        if (null != desc && !"".equals(desc)) {
            sql += "  " + desc;
        }
        Query query =this.session
                .createNativeQuery(sql);
        if (null != obj) {

            for (int i = 0; i < obj.length; i++) {
                query.setParameter(i+1, obj[i]);
            }
        }
        return Integer.parseInt(query.list().size() > 0 ? query.list().get(0).toString() : "0");
    }

    /**
     * TODO 执行一条SQL
     *
     * @param sql
     */
    public void executeSql(String sql) {
       this.session.createNativeQuery(sql)
                .executeUpdate();
    }

    /**
     * TODO 执行一条SQL
     *
     * @param sql sql语句
     * @param obj 参数
     */
    public List executeSqlList(String sql, Object[] obj) {
        Query query =this.session.createNativeQuery(sql);
        if (null != obj) {
            for (int i = 0; i < obj.length; i++) {
                query.setParameter(i+1, obj[i]);
            }
        }
        return query.list();
    }

    /**
     * TODO 执行一条SQL
     *
     * @param sql sql语句
     * @param obj 参数
     */
    public void executeSql(String sql, Object[] obj) {
        Query query =this.session
                .createNativeQuery(sql);
        if (null != obj) {
            for (int i = 0; i < obj.length; i++) {
                query.setParameter(i+1, obj[i]);
            }
        }
        query.executeUpdate();
    }

    /******
     * 传入原始sql语句通过字段名称映射到实体类（必须是数据库对应对象）中
     *
     * ******/
    public <T> List<T> getListByColumSQL(String sql, List<Object> params, int start, int limit, Class<T> claz) throws Exception {
        return getListByColumSQL(sql, params, start, limit, claz, false, "");
    }

    public <T> List<T> getListByColumSQL(String sql, List<Object> params, int start, int limit, Class<T> claz, boolean isDic, String desc) throws Exception {
        String temp = desc.substring(desc.lastIndexOf(desc.trim()));// 去前空格
        if (null != temp && !"".equals(temp)) {
            sql += "  " + temp;
        }
        List<T> list = new ArrayList<>();


        Map<String, Field> fieldMap = getClazFieldMap(claz);
        Map<String, String> mapping = getClazProperty(fieldMap,claz);
        Connection conn = null;

        Query q =this.session
                .createNativeQuery(sql);

        if (params != null) {
            for (int i = 0; i < params.size(); i++) {
                q.setParameter(i+1, params.get(i));
            }
        }
        if (start != 0) {
            q.setFirstResult(start);
        }
        if (limit != 0) {
            q.setMaxResults(limit);
        }
        long startTime = System.currentTimeMillis();
        List<Map<String, Object>> resultList = q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
        for (Map<String, Object> map : resultList) {
            Object object = claz.newInstance();
            for (String columnName : map.keySet()) {
                Field field = fieldMap.get(mapping.get(columnName));
                field = field == null ? fieldMap.get(columnName) : field;
                if (field == null) {
                    continue;
                }

                Object val = map.get(columnName);

                if (isDic) {
                    DictionaryInfoGroupId dictionaryInfoGroupId = field.getAnnotation(DictionaryInfoGroupId.class);
                    if (dictionaryInfoGroupId != null) {
                        val = DataTools.getDicTextByGroupIdAndVal(dictionaryInfoGroupId.groupId(), String.valueOf(val));
                    }
                }


                ClassUtils.setValue(val, object, field);
            }
            list.add((T) object);
        }

        return list;
    }

    /******
     * 传入原始sql语句通过字段名称映射到实体类（非数据库对应对象，保证查询语句中的别名与对象属性一致）中
     *
     * ******/
    public List getListByAliasSQL(String sql, List<Object> params, int start, int limit, Class<?> claz, boolean isDic) {
        return getListByAliasSQL(sql, params, start, limit, claz, isDic, "");
    }

    public List getListByAliasSQL(String sql, List<Object> params, int start, int limit, Class<?> claz, boolean isDic, String desc) {
        String temp = desc.substring(desc.lastIndexOf(desc.trim()));// 去前空格
        if (null != temp && !"".equals(temp)) {
            sql += "  " + temp;
        }

        Connection conn = null;
        List list = new ArrayList<>();

        try {

            Map<String, Field> fieldMap = getClazFieldMap(claz);

            Query q =this.session
                    .createNativeQuery(sql);
            if (params != null) {
                for (int i = 0; i < params.size(); i++) {
                    q.setParameter(i+1, params.get(i));
                }
            }
            if (start != 0) {
                q.setFirstResult(start);
            }
            if (limit != 0) {
                q.setMaxResults(limit);
            }

            List<Map<String, Object>> resultList = q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();

            for (Map<String, Object> map : resultList) {
                Object object = claz.newInstance();
                for (String columnName : map.keySet()) {
                    Field field = fieldMap.get(columnName.toLowerCase());
                    if (field == null) {
                        continue;
                    }

                    Object value = map.get(columnName);

                    if (isDic) {
                        DictionaryInfoGroupId dictionaryInfoGroupId = field.getAnnotation(DictionaryInfoGroupId.class);
                        if (dictionaryInfoGroupId != null) {
                            value = DataTools.getDicTextByGroupIdAndVal(dictionaryInfoGroupId.groupId(), String.valueOf(value));
                        }
                    }

                    ClassUtils.setValue(value, object, field);
                }
                list.add((T) object);
            }
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        } finally {
            if (conn != null) {
                DataSourceUtils.releaseConnection(conn, dataSource);
            }
        }
        return list;
    }

    /*private synchronized Map<String, Field> getClazFieldMap(Class<?> claz){
        Map<String, Field> fieldMap = new HashMap<>();
        if(fieldMapping.containsKey(claz.getName())){
            fieldMap = fieldMapping.get(claz.getName());
        }else{
            List<Field> fieldList = new ArrayList<>();

            fieldList.addAll(Arrays.asList(claz.getDeclaredFields()));
            fieldList.addAll(Arrays.asList(claz.getFields()));

            for (Field field : fieldList) {
                fieldMap.put(field.getName().toLowerCase(), field);
            }

            fieldMapping.put(claz.getName(), fieldMap);
        }

        return fieldMap;
    }*/
    private synchronized Map<String, Field> getClazFieldMap(Class<?> claz) {
        Map<String, Field> fieldMap = new HashMap<>();
        String key = claz.getName();
        if (fieldMapping.containsKey(claz.getName())) {
            fieldMap = fieldMapping.get(claz.getName());
        } else {
            while (true) {
                try {
                    if (claz == null || claz.isInterface() || claz.equals(Object.class)) {
                        break;
                    }
                    List<Field> tempFieldList = Arrays.asList(claz.getDeclaredFields());
                    for (Field field : tempFieldList) {
                        fieldMap.put(field.getName(), field);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

                claz = claz.getSuperclass();

            }
        }
        fieldMapping.put(key, fieldMap);
        return fieldMap;
    }

    private synchronized Map<String, String> getClazProperty(Map<String,Field> fieldMap,Class<?> claz) throws Exception {
        Map<String, String> mapping = new HashMap<>();
        if (propertyMapping.containsKey(claz.getName())) {
            mapping = propertyMapping.get(claz.getName());
        } else {//
            MappingMetamodelImpl metamodel = (MappingMetamodelImpl ) session.getSessionFactory().getMetamodel();
            Map<String, CollectionPersister> map = metamodel.collectionPersisters();
            EntityPersister entityPersister = metamodel.getEntityDescriptor(claz.getName());
            SingleTableEntityPersister singleTableEntityPersister = (SingleTableEntityPersister)entityPersister;
            for(String name:fieldMap.keySet()){
                String[] arr = singleTableEntityPersister.getSubclassPropertyColumnNames(name);
                if(arr == null || arr.length <= 0){
                    continue;
                }
                mapping.put(arr[0],name);
            }

            propertyMapping.put(claz.getName(), mapping);
        }

        return mapping;
    }


    public List getListQueryScaleSQL(String sql, List<Object> params, int start, int limit, String order, Class<?> claz, Map<String, BasicTypeReference> extraQueryTypeMap) throws Exception {
        NativeQuery sqlQuery =this.session
                .createNativeQuery(sql + " " + order);
        Map<String, BasicTypeReference> map = ClassUtils.classToSsqlQuery(claz);
        for (String key : map.keySet()) {
            sqlQuery.addScalar(key, map.get(key));
        }
        if (extraQueryTypeMap != null) {
            for (String key : extraQueryTypeMap.keySet()) {
                sqlQuery.addScalar(key, extraQueryTypeMap.get(key));
            }
        }
        sqlQuery.setResultTransformer(Transformers.aliasToBean(claz));

        if (params != null) {
            for (int i = 0; i < params.size(); i++) {
                sqlQuery.setParameter(i+1, params.get(i));
            }
        }
        if (start != 0) {
            sqlQuery.setFirstResult(start);
        }
        if (limit != 0) {
            sqlQuery.setMaxResults(limit);
        }

        List list = sqlQuery.list();

        if (list == null) {
            return new ArrayList<>();
        }

        return list;
    }

    public Map<String, Object> getListByPage(String hql, String countSql, Object[] params, String orderBy, int start, int limit) {
        Map<String, Object> map = new HashMap<>();

        List objlist = this.getList(hql, params, start, limit, orderBy);
        int sum = this.getListCount(countSql, params, "");
        int count = (int) Math.ceil(Double.valueOf(sum) / Double.valueOf(limit));

        map.put("result", objlist);
        map.put("start", start);
        map.put("limit", limit);
        map.put("count", count);

        return map;
    }


}
